package midend.IR_Optimizer;

import midend.IR_Type.*;
import midend.IR_Value.*;
import midend.IR_Instruction.*;

import java.util.Iterator;
import java.util.List;

/**
 * 常量折叠
 * 常量传播: 在进行mem_to_reg优化之后，一些对0alloc变量的引用变成了常量，有助于常量传播
 */
public class Const_Propagation implements IR_Opt {

    @Override
    public void Optimize(IR_FuncBlock functionBlock) {
        // 遍历函数块中所有的基本块，以及每个基本块中的所有指令，寻找可以进行常量折叠的指令
        for (IR_BaseBlock bb : functionBlock.get_bb_List()) {
            Iterator<Instruction> iterator = bb.get_Instruction_List().iterator();
            while (iterator.hasNext()) {
                Instruction inst = iterator.next();
                if (inst instanceof Calc_Instruction calcInst) {
                    List<IR_Value> ops = calcInst.get_op();
                    IR_Value var = ops.get(0);
                    IR_Value lop = ops.get(1);
                    IR_Value rop = ops.get(2);
                    if (is_Const(lop) && is_Const(rop)) {
                        IR_Value result = foldConstant(calcInst.get_op_Type(), lop, rop);
                        if (result == null) {
                            continue;
                        }
                        iterator.remove(); // 进行常量折叠，这条指令可以删除

                        //替换所有var为result;
                        for (IR_BaseBlock block : functionBlock.get_bb_List()) {
                            for (Instruction ins : block.get_Instruction_List()) {
                                List<IR_Value> op = ins.get_op();

                                // 处理phi指令处的引用
                                if (ins instanceof Phi_Instruction phiInstruction){
                                    for (IR_BaseBlock key : phiInstruction.get_ComingValues().keySet()){
                                        if (phiInstruction.get_ComingValues().get(key).equals(var)){
                                            phiInstruction.set_ComingValue(result, key);
                                        }
                                    }
                                }

                                //处理call函数参数处的引用
                                if (ins instanceof Call_Instruction callInstruction) {
                                    for (int k = 0; k < callInstruction.get_params().size(); k++) {
                                        if (callInstruction.get_params().get(k).equals(var)) {
                                            callInstruction.setParams(k, result);
                                        }
                                    }
                                }

                                for (int k = 0; k < op.size(); k++) {
                                    if (op.get(k) == null) {
                                        continue;
                                    }
                                    if (op.get(k).get_ValueName().equals(var.get_ValueName())) {
                                        if (ins instanceof Pointer_Instruction) {
                                            ((Pointer_Instruction) ins).set_List(k - 2, result);
                                            ins.set_Op(k, result);
                                        } else {
                                            ins.set_Op(k, result);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    //检查值是否为常量
    private boolean is_Const(IR_Value value) {
        return value.get_Const_Type() == IR_Const.IR_ConstInt32 ||
                value.get_Const_Type() == IR_Const.IR_ConstFloat || value.get_Const_Type() == IR_Const.IR_ConstInt1;
    }

    //计算常量表达式结果
    private IR_Value foldConstant(String type, IR_Value lop, IR_Value rop) {
        IR_Type res_Type = lop.get_Type();
        if (res_Type instanceof IR_Float_Value) {
            float lop_Value = Float.parseFloat(lop.get_ValueName());
            float rop_Value = Float.parseFloat(rop.get_ValueName());
            float res_Value = 0;
            switch (type) {
                case "fadd":
                    res_Value = lop_Value + rop_Value;
                    break;
                case "fsub":
                    res_Value = lop_Value - rop_Value;
                    break;
                case "fmul":
                    res_Value = lop_Value * rop_Value;
                    break;
                case "fdiv":
                    res_Value = lop_Value / rop_Value;
                    break;
            }
            return new IR_Float_Value(res_Value);
        } else if (res_Type instanceof IR_Int32) {
            int lop_Value = Integer.parseInt(lop.get_ValueName());
            int rop_Value = Integer.parseInt(rop.get_ValueName());
            int res_Value = 0;
            switch (type) {
                case "add":
                    res_Value = lop_Value + rop_Value;
                    break;
                case "sub":
                    res_Value = lop_Value - rop_Value;
                    break;
                case "mul":
                    res_Value = lop_Value * rop_Value;
                    break;
                case "sdiv":
                    res_Value = lop_Value / rop_Value;
                    break;
                case "srem":
                    res_Value = lop_Value % rop_Value;
                    break;
                case "xor":
                    res_Value = lop_Value ^ rop_Value;
                    break;
            }
            return new IR_Int_Value(res_Value, IR_Int32.IR_Int32());
        }
        return null;
    }



}
